//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<CDevice10.h>
///	@path	~/src/graphics/dx10/
///	@date	2007/09/16
///	@desc	DirectX 10 device implementation of IDevice.

#pragma once

#include "xeres.h"

// DirectX 9 is also needed
#include <d3d9.h>

// DirectX 10 header files
#include <d3d10.h>

// STL
#include <vector>

#include "graphics/IDevice.h"
#include "graphics/IDeviceCallback.h"
#include "graphics/animation/frame.h"

#include "lib/system/lock.h"
#include "lib/utilities/string.h"

namespace xeres {

	/*!
		\class	CDevice10
		\brief	We do most DX10 device management in this class.
	*/
	class CDevice10 : public IDevice
	{
	public:

		/// \ctor
		CDevice10( void );

		/// \dtor
		virtual ~CDevice10( void );

		DECL_IOBJECT_CLASS_DYNAMIC( CDevice10 , IDevice );

	public:

	// interface

		/*!
			\brief		Initialize device from a given window & configuration.
			\param[in]	window		Handle of target window.
			\param[in]	config		Device configuration.
			\return					Result of initialization.
		*/
		virtual bool Initialize( void * window , RefWeak<IData> config );

		/*!
			\brief		Finalize device & its context.
		*/
		virtual void Finalize( void );

		/*!
			\brief		Tick call, give a chance to update device states.
		*/
		virtual void Tick( const Frame& frame );

		/*!
			\brief		Change full screen state.
		*/
		virtual bool ToggleFullScreen( void * window );

		/*!
			\brief		Determine if currently is full screen view.
		*/
		virtual bool IsFullScreen( void );

		/*!
			\brief		Attach device callback.
			\param[in]	callback	Callback instance.
		*/
		virtual void AttachCallback( RefWeak<IDeviceCallback> callback );

		/*!
			\brief		Call to enter device sizing.
		*/
		virtual void EnterSizing( void );

		/*!
			\brief		Call to exit device sizing.
		*/
		virtual void ExitSizing( void );

		/*!
			\brief		Call when window size maybe changed.
		*/
		virtual void OnSize( bool minized );

		/*!
			\brief		Called before start to render scene.
		*/
		virtual bool IsEnableRender( void );

		/*!
			\brief		Call to begin render.
						Return result must be checked,
						while false indicates the device is changing
						that device should not be render.
			\return		Enable render?
		*/
		virtual bool BeginRender( void );

		/*!
			\brief		Call to end render & refresh representation.
		*/
		virtual void EndRender( void );

		/*! 
			\brief		Call to query back buffer size.
		*/
		virtual Pair<int> GetBackBufferSize( void );

	// Device manipulation

		/*!
			\brief		Get raw dx10 interface.
		*/
		ID3D10Device * GetDevice( void ) const { return m_d3dDevice; }

		/*!
			\brief		Direct access to IDirect3DDevice9 interface.
			\return		Interface of D3D10.
		*/
		ID3D10Device * operator -> ( void ) const { return m_d3dDevice; }

		/*!
			\brief		Get primary screen resolution.
		*/
		void GetResolution( int& xres , int& yres );

	private:

		// device settings
		struct _DeviceSettings
		{
			UINT						adapterOrdinal;
			D3D10_DRIVER_TYPE       	driverType;
			UINT						output;
			UINT32						createFlags;
			DWORD						presentFlags;
			UINT32						syncInterval;
			DXGI_FORMAT					autoDepthStencilFormat;
			BOOL						autoCreateDepthStencil;
			DXGI_SWAP_CHAIN_DESC		swapChainDesc;
		};

		// device match type
		enum _MATCH_TYPE
		{
			MATCH_IGNORE_INPUT = 0,  // Use the closest valid value to a default 
			MATCH_PRESERVE_INPUT,    // Use input without change, but may cause no valid device to be found
			MATCH_CLOSEST_TO_INPUT   // Use the closest valid value to the input 
		};

		// device match options
		struct _MatchOptions
		{
			_MATCH_TYPE AdapterOrdinal;
			_MATCH_TYPE Output;
			_MATCH_TYPE DeviceType;
			_MATCH_TYPE Windowed;
			_MATCH_TYPE AdapterFormat;
			_MATCH_TYPE VertexProcessing;
			_MATCH_TYPE Resolution;
			_MATCH_TYPE BackBufferFormat;
			_MATCH_TYPE BackBufferCount;
			_MATCH_TYPE MultiSample;
			_MATCH_TYPE SwapEffect;
			_MATCH_TYPE DepthFormat;
			_MATCH_TYPE StencilFormat;
			_MATCH_TYPE PresentFlags;
			_MATCH_TYPE RefreshRate;
			_MATCH_TYPE PresentInterval;
		};

		// is device windowed
		static inline bool is_windowed( _DeviceSettings * );

		// ensure DirectX 10 device
		static bool CheckDeviceVersion( void );

		// check for size change
		void CheckForSizeChange( void );

		// select proper device mode
		bool SelectMode( _DeviceSettings * settings );

		// determine if device can be reset
		bool CanDeviceBeReset( _DeviceSettings * oldSettings , _DeviceSettings * newSettings );

		// change device mode
		HRESULT ChangeMode( _DeviceSettings * newSettings );

		// setup views
		HRESULT SetupViews( void );

		// reset environment
		HRESULT ResetEnvironment( void );

		// cleanup environment
		void CleanupEnvironment( bool resetSettings );

		// create environment
		HRESULT CreateEnvironment( void );

		// get monitor handle
		HMONITOR GetMonitorFromAdapter( void );

		// update back buffer desc
		void UpdateBackBufferDesc( void );

		// find valid device settings
		bool FindValidSettings( _DeviceSettings * prefered , _DeviceSettings * output , _DeviceSettings * optimal ,
			const _MatchOptions * options );

		// build optimal settings
		void BuildOptimalSettings( _DeviceSettings * prefered , _DeviceSettings * optimal ,
			const _MatchOptions * options );

		void ResizeDXGIBuffers( uint width , uint height , bool fullscreen );

		// Get adapter display mode.
		void GetAdapterDisplayMode( UINT AdapterOrdinal, UINT nOutput, DXGI_MODE_DESC& pModeDesc );

	// data

		// operation lock
		ThreadLock					m_lock;

		// data
		HWND                    	m_hWnd;
		HMENU						m_hMenu;
		HMONITOR					m_hAdapterMonitor;

		_DeviceSettings				m_currentSettings;

		ID3D10Device *          	m_d3dDevice;
		IDXGIFactory *				m_dxgiFactory;
		IDXGISwapChain *        	m_swapChain;
		IDXGIAdapter *				m_adapter;
		ID3D10Texture2D *			m_depthStencil;
        ID3D10DepthStencilView *	m_depthStencilView;
		ID3D10RenderTargetView * 	m_renderTargetView;
		ID3D10RasterizerState *		m_rasterizerState;

		DXGI_SURFACE_DESC			m_backBufferSurfaceDesc;
		
		// outputs
		std::vector<IDXGIOutput*>	m_outputs;

		// device states
		UINT 						m_fullScreenBackBufferWidthAtModeChange;
        UINT 						m_fullScreenBackBufferHeightAtModeChange;
        UINT 						m_windowBackBufferWidthAtModeChange;
        UINT 						m_windowBackBufferHeightAtModeChange;
        DWORD						m_windowedStyleAtModeChange;
        WINDOWPLACEMENT				m_windowedPlacement;

		bool						m_settingsValid;		// Device settings is valid
		bool						m_ignoreSizeChange;		// Set to make size change call ignored
		bool						m_doNotStoreBufferSize;	// Do not store the buffer size on window size change
		bool						m_noUpdate;				// Device cannot be updated

		bool						m_deviceCreated;
		bool						m_releasingSwapChain;
		bool						m_deviceLost;			// If true, then the device is lost and needs to be reset
		bool						m_insideDeviceCallback;

		bool						m_minimized;
		bool						m_enablePerf;
		bool						m_enableStat;
		bool						m_usePerfHud;

		// statistics
		struct _D3D10Counters
		{
			float 	gpuIdle;
			float 	vertexProcessing;
			float 	geometryProcessing;
			float 	pixelProcessing;
			float 	otherGPUProcessing;
			float 	hostAdapterBandwidthUtilization;
			float 	localVidmemBandwidthUtilization;
			float 	vertexThroughputUtilization;
			float 	triangleSetupThroughputUtilization;
			float 	fillrateThroughputUtilization;
			float 	vsMemoryLimited;
			float 	vsComputationLimited;
			float 	gsMemoryLimited;
			float 	gsComputationLimited;
			float 	psMemoryLimited;
			float 	psComputationLimited;
			float 	postTransformCacheHitRate;
			float 	textureCacheHitRate;
		};

		ID3D10Counter *			m_Counter_GPUIdle;
        ID3D10Counter *			m_Counter_VertexProcessing;
        ID3D10Counter *			m_Counter_GeometryProcessing;
        ID3D10Counter *			m_Counter_PixelProcessing;
        ID3D10Counter *			m_Counter_OtherGPUProcessing;
        ID3D10Counter *			m_Counter_HostAdapterBandwidthUtilization;
        ID3D10Counter *			m_Counter_LocalVidmemBandwidthUtilization;
        ID3D10Counter *			m_Counter_VertexThroughputUtilization;
        ID3D10Counter *			m_Counter_TriangleSetupThroughputUtilization;
        ID3D10Counter *			m_Counter_FillrateThrougputUtilization;
        ID3D10Counter *			m_Counter_VSMemoryLimited;
        ID3D10Counter *			m_Counter_VSComputationLimited;
        ID3D10Counter *			m_Counter_GSMemoryLimited;
        ID3D10Counter *			m_Counter_GSComputationLimited;
        ID3D10Counter *			m_Counter_PSMemoryLimited;
        ID3D10Counter *			m_Counter_PSComputationLimited;
        ID3D10Counter *			m_Counter_PostTransformCacheHitRate;
        ID3D10Counter *			m_Counter_TextureCacheHitRate;
        _D3D10Counters			m_CounterData;

		// callbacks
		std::vector< Ref<IDeviceCallback> > m_callbacks;
	};
}
